package com.seecoder.BlueWhale.serviceImpl;

import com.seecoder.BlueWhale.annotation.RoleAccessCheck;
import com.seecoder.BlueWhale.enums.*;
import com.seecoder.BlueWhale.exception.BlueWhaleException;
import com.seecoder.BlueWhale.po.Coupon;
import com.seecoder.BlueWhale.po.CouponGroup;
import com.seecoder.BlueWhale.po.Store;
import com.seecoder.BlueWhale.po.User;
import com.seecoder.BlueWhale.repository.CouponGroupRepository;
import com.seecoder.BlueWhale.repository.CouponRepository;
import com.seecoder.BlueWhale.repository.StoreRepository;
import com.seecoder.BlueWhale.service.CouponGroupService;
import com.seecoder.BlueWhale.serviceImpl.calPriceStrategy.*;
import com.seecoder.BlueWhale.util.SecurityUtil;
import com.seecoder.BlueWhale.vo.CouponGroupVO;
import com.seecoder.BlueWhale.vo.CouponVO;
import com.seecoder.BlueWhale.vo.DiscountParams;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class CouponGroupServiceImpl implements CouponGroupService {
    @Autowired
    CouponGroupRepository couponGroupRepository;
    @Autowired
    SecurityUtil securityUtil;
    @Autowired
    StoreRepository storeRepository;
    @Autowired
    CouponRepository couponRepository;


    @Override
    @RoleAccessCheck(role = RoleEnum.CEO)
    public CouponGroupVO ceoCreate(CouponGroupVO couponGroupVO) {
        if (couponGroupVO.getCouponCount() <= 0) {
            throw BlueWhaleException.invalidInput();
        }
        int id = couponGroupVO.getStoreId();
        if (id == 0) {
            couponGroupVO.setCouponScope(CouponScopeEnum.GLOBAL);
        } else {
            couponGroupVO.setCouponScope(CouponScopeEnum.LOCAL);
            Store store = storeRepository.findById(id);
            if (store == null) {
                throw BlueWhaleException.cannotFindStore();
            }
        }
        CouponGroup newCouponGroup = couponGroupVO.toPO();
        newCouponGroup.setCouponGroupStatusEnum(CouponGroupStatusEnum.REMAINING);
        couponGroupRepository.save(newCouponGroup);
        return newCouponGroup.toVO();
    }

    @Override
    @RoleAccessCheck(role = RoleEnum.STAFF)
    public CouponGroupVO createLocal(CouponGroupVO couponGroupVO) {
        if (couponGroupVO.getCouponCount() <= 0) {
            throw BlueWhaleException.invalidInput();
        }
        couponGroupVO.setCouponScope(CouponScopeEnum.LOCAL);
        CouponGroup newCouponGroup = couponGroupVO.toPO();
        int storeId = securityUtil.getCurrentUser().getStoreId();
        newCouponGroup.setStoreId(storeId);
        newCouponGroup.setCouponGroupStatusEnum(CouponGroupStatusEnum.REMAINING);
        couponGroupRepository.save(newCouponGroup);
        return newCouponGroup.toVO();
    }

    @Override
    public List<CouponGroupVO> info() {
        User cur = securityUtil.getCurrentUser();
        switch (cur.getRole()) {
            case MANAGER:
                throw BlueWhaleException.managerAccessInvalid();
            case STAFF:
                return couponGroupRepository.findAllByStoreId(cur.getStoreId()).stream().map(CouponGroup::toVO).collect(Collectors.toList());
            default:
                return couponGroupRepository.findAll().stream().map(CouponGroup::toVO).collect(Collectors.toList());
        }
    }

    @Override
    public CouponGroupVO getCouponGroup(int id) {
        CouponGroup couponGroup = findCouponGroupById(id);
        CouponGroupVO couponGroupVO = couponGroup.toVO();
        User cur = securityUtil.getCurrentUser();
        List<Integer> userId = couponGroup.getUserId();
        if (cur.getRole() == RoleEnum.CUSTOMER && userId != null && userId.contains(cur.getId())) {
            couponGroupVO.setUserStatus(UserStatusEnum.RECEIVED);
        }
        return couponGroupVO;
    }

    @Override
    public CouponGroupVO receive(int id) {
        CouponGroup couponGroup = findCouponGroupById(id);
        if (couponGroup.getCouponGroupStatusEnum() == CouponGroupStatusEnum.NOT_REMAINING) {
            throw new BlueWhaleException("已领完！");
        }
        List<Integer> userIdList = couponGroup.getUserId();
        if (userIdList == null) {
            userIdList = new ArrayList<>();
        }
        int userId = securityUtil.getUserId();
        if (userIdList.contains(userId)) {
            throw new BlueWhaleException("已领取该优惠券！");
        }
        userIdList.add(userId);
        couponGroup.setUserId(userIdList);
        couponGroup.setReceivedCount(couponGroup.getReceivedCount() + 1);
        if (couponGroup.getReceivedCount() == couponGroup.getCouponCount()) {
            couponGroup.setCouponGroupStatusEnum(CouponGroupStatusEnum.NOT_REMAINING);
        }
        couponGroupRepository.save(couponGroup);
        CouponVO couponVO = new CouponVO(id, userId);
        Coupon coupon = couponVO.toPO();
        couponRepository.save(coupon);
        return couponGroup.toVO();
    }

    @Override
    public Double discount(DiscountParams discountParams) {
        List<Integer> couponGroupID = discountParams.getCouponGroupID();
        double curPrice = discountParams.getPrice();

        if (couponGroupID.isEmpty()) {
            return curPrice;
        }

        List<CouponGroup> couponGroups = couponGroupID.stream().map(id->couponGroupRepository.findById(id.intValue())).collect(Collectors.toList());
        return calBestDiscount(couponGroups, curPrice);
    }

    /**
     * Use dfs to find the best discount
     * @param couponGroups 优惠券组
     * @param curPrice 当前价格,用于递归
     * @return 最优价格
    */
    private double calBestDiscount(List<CouponGroup> couponGroups, double curPrice) {
        double lowestPrice = curPrice;
        for (int i = 0; i < couponGroups.size(); i++) {
            double nextPrice = oneCoupon(couponGroups.get(i), curPrice);
            CouponGroup tmp = couponGroups.remove(i);
            nextPrice = calBestDiscount(couponGroups, nextPrice);
            couponGroups.add(i, tmp);
            if (nextPrice < lowestPrice) {
                lowestPrice = nextPrice;
            }
        }
        return lowestPrice;
    }

    /**
     *  通过id查找优惠券组
     *  @param id 优惠券组id
     *  @return 优惠券组
    */
    private CouponGroup findCouponGroupById(int id) {
        CouponGroup couponGroup = couponGroupRepository.findById(id);
        if (couponGroup == null) throw BlueWhaleException.cannotFindCouponGroup();
        return couponGroup;
    }

    /**
     *  计算使用一张优惠券后的价格
     *  @param couponGroup 优惠券组
     *  @param price 当前价格
     *  @return 优惠后价格
    */
    private Double oneCoupon(CouponGroup couponGroup, double price) {

        CalculateStrategy calculateStrategy;
        switch (couponGroup.getCouponType()) {
            case NO_LIMIT:
                calculateStrategy = new NoLimitCouponCalculateStrategy(couponGroup.getDiscountAmount());
                break;
            case FULL_REDUCTION:
                calculateStrategy = new FullReductionCouponCalculateStrategy(couponGroup.getFullAmount(), couponGroup.getDiscountAmount());
                break;
            case SPECIAL:
                calculateStrategy = new SpecialCouponCalculateStrategy();
                break;
            default:
                throw BlueWhaleException.cannotFindCouponGroup();
        }
        return new CalPriceContext(calculateStrategy).calPrice(price);
    }
}
